#include <linux/vmalloc.h>
#include "rxe.h"
#include "rxe_loc.h"
#include "rxe_queue.h"

int do_mmap_info(struct rxe_dev *rxe, struct mminfo __user *outbuf,
        struct ib_udata *udata, struct rxe_queue_buf *buf,
        size_t buf_size, struct rxe_mmap_info **ip_p)
{
    int err;
    struct rxe_mmap_info *ip = NULL;

    if(outbuf) {
        /*计算ip->info的值*/
        ip = rxe_create_mmap_info(rxe, buf_size, udata, buf);
        if(IS_ERR(ip)) {
            err = PTR_ERR(ip);
            goto err1;
        }
        /*将ip->info的值拷贝到用户空间*/
        if(copy_to_user(outbuf, &ip->info, sizeof(ip->info))) {
            err = -EFAULT;
            goto err2;
        }

        /*加入全局的mmap链表里*/
        spin_lock_bh(&rxe->pending_lock);
        list_add(&ip->pending_mmaps, &rxe->pending_mmaps);
        spin_unlock_bh(&rxe->pending_lock);
    }

    /*这个做为输出参数给出去貌似没有什么用*/
    *ip_p = ip;
    return 0;
err2:
    kfree(ip);
err1:
    return err;
}

inline void rxe_queue_reset(struct rxe_queue *q)
{
    memset(q->buf->data, 0, q->buf_size - sizeof(struct rxe_queue_buf));
}

struct rxe_queue *rxe_queue_init(struct rxe_dev *rxe, int *num_elem,/*队列元素个数*/
        unsigned int elem_size/*队列每个元素的大小*/, enum queue_type type)
{
    struct rxe_queue *q;
    size_t buf_size;
    unsigned int num_slots;

    if(*num_elem < 0)
        goto err1;

    q = kzalloc(sizeof(*q), GFP_KERNEL);
    if(!q)
        goto err1;

    q->type = type;
    q->rxe = rxe;

    q->elem_size = elem_size;
    if(elem_size < cache_line_size())//64
        elem_size = cache_line_size();

    elem_size = roundup_pow_of_two(elem_size);//返回值保证是2的次幂
    q->log2_elem_size = order_base_2(elem_size);//保证返回值是2为底的对数

    num_slots = *num_elem + 1;
    num_slots = roundup_pow_of_two(num_slots);
    q->index_mask = num_slots - 1;

    /*buf的最上面是struct rxe_queue_buf结构体,wqe元素从q->data开始取址*/
    buf_size = sizeof(struct rxe_queue_buf) + num_slots * elem_size;
    q->buf = vmalloc_user(buf_size);
    if(!q->buf)
        goto err2;
    q->buf->log2_elem_size = q->log2_elem_size;
    q->buf->index_mask = q->index_mask;
    q->buf_size = buf_size;

    *num_elem = num_slots - 1;

    return q;
err2:
    kfree(q);
err1:
    return NULL;
}

static int resize_finish(struct rxe_queue *q, struct rxe_queue *new_q,
        unsigned int num_elem)
{
    enum queue_type type = q->type;
    u32 cons, prod;

    /*扩大队列元素的前提是
    1：
    2: */
    if (!queue_empty(q, q->type) && (num_elem < queue_count(q, type)))
        return -EINVAL;

    /*新队列的pidx*/
    prod = queue_get_producer(new_q, type);
    /*旧队列的cidx*/
    cons = queue_get_consumer(q, type);

    while (!queue_empty(q, type)) {
        memcpy(queue_addr_from_index(new_q, prod), queue_addr_from_index(q, cons),
            new_q->elem_size);
        prod = queue_next_index(new_q, prod);
        cons = queue_next_index(q, cons);
    }

    new_q->buf->producer_index = prod;
    q->buf->consumer_index = cons;

    /* update private index copies */
    if (type == QUEUE_TYPE_TO_CLIENT)
        new_q->index = new_q->buf->producer_index;
    else
        q->index = q->buf->consumer_index;

    /* exchange rxe_queue headers */
    swap(*q, *new_q);

    return 0;
}

int rxe_queue_resize(struct rxe_queue *q, unsigned int *num_elem_p,
        unsigned int elem_size, struct ib_udata *udata,
        struct mminfo __user *outbuf, spinlock_t *producer_lock,
        spinlock_t *consumer_lock)
{
    int err;
    struct rxe_queue *new_q;
    unsigned int num_elem = *num_elem_p;
    unsigned long flags = 0, flags1;

    new_q = rxe_queue_init(q->rxe, &num_elem, elem_size, q->type);
    if (!new_q)
        return -ENOMEM;

    err = do_mmap_info(new_q->rxe, outbuf, udata, new_q->buf,
        new_q->buf_size, &new_q->ip);
    if (err) {
        vfree(new_q->buf);
        kfree(new_q);
        goto err1;
    }

    /*这个上锁就可以了，因为是从消费者开始拷贝数据,
    所以不用关心producer_lock是否必须上锁*/
    spin_lock_irqsave(consumer_lock, flags1);
    if (producer_lock) {
        spin_lock_irqsave(producer_lock, flags);
        err = resize_finish(q, new_q, num_elem);
        spin_unlock_irqrestore(producer_lock, flags);
    } else {
        err = resize_finish(q, new_q, num_elem);
    }
    spin_unlock_irqrestore(consumer_lock, flags1);

    //数据已经交换到原来的数据结构了,
    rxe_queue_cleanup(new_q); /* new/old dep on err */
    if (err)
        goto err1;

    *num_elem_p = num_elem;
    return 0;
err1:
    return err1;

}

void rxe_queue_cleanup(struct rxe_queue *q)
{
    if(q->ip)
        kref_put(&q->ip->ref, rxe_mmap_release);
    else
        vfree(q->buf);

    kfree(q);
}
